using System;
using EscapeZone.Screens;
using EscapeZone.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using XnaPlus.Common.Visual;
using XnaPlus.StateManagement;
using XnaPlus.Windows.Input;

namespace EscapeZone
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class EscapeGame : StateGame
    {
        GraphicsDeviceManager m_graphics;

        public Rectangle Bounds { get { return m_graphics.GraphicsDevice.Viewport.Bounds; } }

        public GameScreen GameScreen { get; private set; }

        public SpriteBatch SpriteBatch { get; private set; }

        public InputState InputState { get; private set; }

        public new GameState State { get { return (GameState)base.State; } }

        private ShapeDrawer m_drawer;

        public ShapeDrawer Drawer
        {
            get { return (m_drawer ?? (m_drawer = new ShapeDrawer(GraphicsDevice))); }
        }

        private Cursor m_cursor;
        private Effect ShadowEffect;
        private Effect FogOfWarEffect;
        private RenderTarget2D SceneTarget;
        private RenderTarget2D ShadowTarget;
        private RenderTarget2D FogTarget;
        private SpriteFont defaultFont;

        public EscapeGame()
        {
            m_graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            m_graphics.PreferredBackBufferHeight = 700;
            m_graphics.PreferredBackBufferWidth = 1150;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            InputState = new InputState(this);
            base.Initialize();
        }

        public void StartGame()
        {
            ScreenManager.AddScreen(GameScreen);
            ChangeState(GameState.Playing);
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            SpriteBatch = new SpriteBatch(GraphicsDevice);

            Art.Pixel = Content.Load<Texture2D>("pixel");
            Art.Player = Content.Load<Texture2D>("player-anim");
            Art.Coin = Content.Load<Texture2D>("coin");
            Art.Cursor = Content.Load<Texture2D>("cursor");
            Art.Guard = Content.Load<Texture2D>("guard-anim");

            defaultFont = Content.Load<SpriteFont>("default");

            SceneTarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
            ShadowTarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
            FogTarget = new RenderTarget2D(GraphicsDevice, GraphicsDevice.PresentationParameters.BackBufferWidth, GraphicsDevice.PresentationParameters.BackBufferHeight, false, SurfaceFormat.Color, DepthFormat.None);
            
            float aspectRatio = (float) Bounds.Width/Bounds.Height;
            ShadowEffect = Content.Load<Effect>("Colorizer");
            ShadowEffect.Parameters["AspectRatio"].SetValue(aspectRatio);
            FogOfWarEffect = Content.Load<Effect>("FogOfWar");
            FogOfWarEffect.Parameters["AspectRatio"].SetValue(aspectRatio);

            //and at the end of this. 
            OnContentLoaded();
        }

        private void OnContentLoaded()
        {
            GameScreen = new GameScreen(this, GameState.Playing);
            GameScreen.Map = new MapSerializer().LoadMap(this, GameScreen, "level1");
            GameScreen.Map.Initialize();

            m_cursor = new Cursor(this, Art.Cursor) { Color = Color.Turquoise};

            StartGame();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            Content.Unload();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }


        public bool IsState(GameState state)
        {
            return IsState((ulong) state);
        }

        public void ChangeState(GameState state)
        {
            ChangeState((ulong)state);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // Compute some parameters for the shaders
            Vector2 playerPos = new Vector2(GameScreen.Player.Position.X / Bounds.Width, GameScreen.Player.Position.Y / Bounds.Height);
            ShadowEffect.Parameters["PlayerPosition"].SetValue(playerPos);
            FogOfWarEffect.Parameters["PlayerPosition"].SetValue(playerPos);

            float playerLookAngle = (float)Math.Atan2(InputState.MousePosition.X - GameScreen.Player.Position.X, InputState.MousePosition.Y - GameScreen.Player.Position.Y);
            FogOfWarEffect.Parameters["PlayerLookAngle"].SetValue(playerLookAngle);

            // Render the scene on an offscreen target
            GraphicsDevice.SetRenderTarget(SceneTarget);
            SpriteBatch.Begin();
            GraphicsDevice.Clear(Color.Transparent); 
            ScreenManager.Draw(gameTime);
            SpriteBatch.End();
            GraphicsDevice.SetRenderTarget(null);

            // Render a small shadow around objects
            GraphicsDevice.SetRenderTarget(ShadowTarget);
            SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, null, null, ShadowEffect);
            GraphicsDevice.Clear(Color.Transparent);
            SpriteBatch.Draw(SceneTarget, Vector2.Zero, Color.White);
            SpriteBatch.End();
            GraphicsDevice.SetRenderTarget(null);

            // Render the lighting effects
            GraphicsDevice.SetRenderTarget(FogTarget);
            SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, null, null, FogOfWarEffect);
            GraphicsDevice.Clear(Color.Transparent);
            SpriteBatch.Draw(SceneTarget, Vector2.Zero, Color.White);
            SpriteBatch.End();
            GraphicsDevice.SetRenderTarget(null);

            // Merge it all and print it to screen
            SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
            GraphicsDevice.Clear(Color.CornflowerBlue);
            SpriteBatch.Draw(ShadowTarget, Vector2.Zero, Color.White);
            SpriteBatch.Draw(SceneTarget, Vector2.Zero, Color.White);
            SpriteBatch.Draw(FogTarget, Vector2.Zero, Color.White);

            // Draw the 2D UI overlay
            Vector2 scorePosition = new Vector2(10.0f, Bounds.Bottom - 50.0f);
            if (GameScreen.Player.Score < 50)
                SpriteBatch.DrawString(defaultFont, GameScreen.Player.Score.ToString("G"), scorePosition, Color.White);
            else
                SpriteBatch.DrawString(defaultFont, "Find the end zone", scorePosition, Color.White);

            m_cursor.Draw(gameTime);

            SpriteBatch.End();
        }
    }
}
